﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using eProject.Model;
using eProject.ViewModel;
using eProject.ViewModel.Commands;
using Point=System.Windows.Point;
 
namespace eProject.View
{
    /// <summary>
    /// Interaction logic for ActivitiesView.xaml
    /// </summary>
    public partial class ActivitiesView : INotifyPropertyChanged
    {
        #region Constructor

        public ActivitiesView()
        {
            InitializeComponent();
        }

        #endregion

        #region Properties

        public static readonly DependencyProperty CommonManagerProperty = DependencyProperty.Register("CommonManager",
                                                                                                      typeof (
                                                                                                          CommonManager),
                                                                                                      typeof (
                                                                                                          ActivitiesView
                                                                                                          ));
        public static readonly DependencyProperty OwnerWindowProperty =
          DependencyProperty.Register("OwnerWindow", typeof(Window), typeof(ActivitiesView));

        public CommonManager CommonManager
        {
            get { return (CommonManager)GetValue(CommonManagerProperty); }
            set { SetValue(CommonManagerProperty, value); }
        }

        

        public Window OwnerWindow
        {
            get { return (Window)GetValue(OwnerWindowProperty); }
            set { SetValue(OwnerWindowProperty, value); }
        }

        public bool IsLinked { get; private set; }

        //public static DependencyProperty EmployeeProperty = DependencyProperty.Register("Employee",
        //                                                                                typeof (EmployeeViewModel),
                                                                                        //typeof (ActivitiesView));

        public EmployeeViewModel Employee
        {
            get { return _employee; }
            private set
            {
                if (value == _employee) return;
                _employee = value;
                InvokePropertyChanged(new PropertyChangedEventArgs("Employee"));
            }
        }

        public TaskViewModel Task
        {
            get { return _task; }
            private set
            {
                if (value == _task) return;
                _task = value;
                InvokePropertyChanged(new PropertyChangedEventArgs("Task"));
            }
        }


        public double CanvasHeight
        {
            get { return _canvasHeight; }
            private set
            {
                if (_canvasHeight == value) return;
                _canvasHeight = value;
                InvokePropertyChanged(new PropertyChangedEventArgs("CanvasHeight"));

            }
        }

        public double CanvasWidth
        {
            get { return _canvasWidth; }
            set
            {
                if (_canvasWidth == value) return;
                _canvasWidth = value;

                InvokePropertyChanged(new PropertyChangedEventArgs("CanvasWidth"));
            }
        }

        public double ScrollWidth
        {
            get { return _scrollWidth; }
            set
            {
                if (_scrollWidth == value) return;
                _scrollWidth = value;

                InvokePropertyChanged(new PropertyChangedEventArgs("ScrollWidth"));
            }
        }

        public bool IsDetailed 
        {
            get { return _isDetailed; }
            set
            {
                if (_isDetailed == value) return;
                _isDetailed = value;
                InvokePropertyChanged(new PropertyChangedEventArgs("IsDetailed"));
            }
        }


        #endregion

        #region Events

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
        
        #region Methods

        public void LinkTo(CommonManager manager, CommandsQueue queue)
        {
            if (manager == null) throw new ArgumentNullException("manager");
            if (queue == null) throw new ArgumentNullException("queue");
            if (_manager != null) Unlink();
            _manager = manager;
            _commandsQueue = queue;
            
            _manager.PeriodChanged += OnPeriodChanged;
            SizeChanged += OnSizeChanged;
            INotifyCollectionChanged iacts = _manager.Activities;
            iacts.CollectionChanged += OnItemsCollectionChanged;
            DataContext = _manager.Activities;
            RefreshWidth();
            RefreshHeight();
        }


        public void Unlink()
        {
            if (!IsLinked) return;
            DataContext = null;
            _manager.PeriodChanged -= OnPeriodChanged;
            SizeChanged -= OnSizeChanged;
            INotifyCollectionChanged iitems = _manager.Activities;
            iitems.CollectionChanged -= OnItemsCollectionChanged;            
            _manager = null;
            _commandsQueue = null;
            IsLinked = false;
        }

        public void ShowBy(TaskViewModel task)
        {            
            _mode = Modes.ByTasks;
            Task = task;
            Employee = null;
        }

        public void ShowBy(EmployeeViewModel employee)
        {
            _mode = Modes.ByEmployees;
            Employee = employee;
            Task = null;
        }

        public void SetViewMode(Modes mode) //TODO replace by binding
        {
            _mode = mode;
            DataTemplate template = null;
            switch (mode)
            {
                case Modes.ByEmployees:                    
                    template = Resources["toTasks"] as DataTemplate;                    
                    break;
                case Modes.ByTasks:                    
                    template = Resources["toEmployees"] as DataTemplate;                    
                    break;
            }
            if (template != null) listBox.ItemTemplate = template;
        }        

        private void RefreshWidth()
        {
            CanvasWidth = Math.Truncate(Math.Max(_manager.Period.Duration * ViewConstants.PixelsPerDay, ActualWidth));
            ScrollWidth = Math.Max(CanvasWidth - ActualWidth, 0);
        }

        private void RefreshHeight()
        {
            CanvasHeight = Math.Truncate(Math.Max(_manager.Activities.Count * ViewConstants.PixelsPerLine, ActualHeight - 4));//TODO strange thing            
        }

        private void InvokePropertyChanged(PropertyChangedEventArgs e)
        {
            var changed = PropertyChanged;
            if (changed != null) changed(this, e);
        }

        private void EditCurrentActivity()
        {
            var act = listBox.SelectedItem as ActivityViewModel;
            if (act == null) return;
            var dialog = new CreateActivityDialog(_manager, act){Owner = OwnerWindow};
            var result = dialog.ShowDialog();

            if (result == true)
            {
                _commandsQueue.Do(new ChangeActivityCommand(_manager, act, dialog.Period, dialog.Priority, dialog.Description));
            }
        }

        #endregion

        #region Event Handlers

        private void activityView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            EditCurrentActivity();
            e.Handled = true;
        }

        private void CommandBinding_EditCommand(object sender, ExecutedRoutedEventArgs e)
        {
            EditCurrentActivity();
        }

        private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            RefreshHeight();
        }

        private void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
        	if (e.NewSize.Width != e.PreviousSize.Width) RefreshWidth();
        	if (e.NewSize.Height != e.PreviousSize.Height) RefreshHeight();
        }

        private void OnPeriodChanged(Period oldOne, Period newOne)
        {
            RefreshWidth();
        }

        private void CommandBinding_CreateCommand(object sender, ExecutedRoutedEventArgs e)
        {
            var task = Task;
            var employee = Employee;
            var date = DateTime.Now.Date;
            if (e.Parameter != null)
            {
                var pos = (Point)e.Parameter;
                var index = (int)Math.Truncate((pos.Y - ViewConstants.TimeGridLabelPadding) / ViewConstants.PixelsPerLine);
                if (_mode == Modes.ByEmployees && (index < 0 || index >= _manager.Tasks.Count)) return;
                if (_mode == Modes.ByTasks && (index < 0 || index >= _manager.Employees.Count)) return;
                date = _manager.Period.Begin.AddDays(Math.Ceiling(pos.X / ViewConstants.PixelsPerDay));
                switch (_mode)
                {
                    case Modes.ByTasks:
                        employee = _manager.Employees[index];
                        task = Task;
                        break;
                    case Modes.ByEmployees:
                        employee = Employee;
                        task = _manager.Tasks[index]; 
                        break;
                }
            }           

            var dialog = new CreateActivityDialog(_manager) { Begin = date, End = date.AddDays(1), Task = task ?? _manager.Tasks[0], Employee = employee ?? _manager.Employees[0], Owner = OwnerWindow};
            var result = dialog.ShowDialog();
            if (result == true)
            {
                var resTask = dialog.Task;
                var resEmpl = dialog.Employee;
                var period = dialog.Period;
                var priority = dialog.Priority;
                var description = dialog.Description;
                _commandsQueue.Do(new CreateActivityCommand(_manager, resTask, resEmpl, period, priority, description));
            }
        }

        private void CommandBinding_DeleteCommand(object sender, ExecutedRoutedEventArgs e)
        {
            var item = listBox.SelectedItem;
            var act = item as ActivityViewModel;
            _commandsQueue.Do(new DeleteActivityCommand(_manager, act));
        }

        private void CommandBinding_DeleteCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (listBox == null) return;
            var item = listBox.SelectedItem;
            var act = item as ActivityViewModel;
            e.CanExecute = act != null;
        }

        private void listBox_MouseDown(object sender, MouseButtonEventArgs e)
        {            
            if (_currentActivityView == null)
                Keyboard.Focus(listBox);
        }

        #region Move&Resize

        private void Rectangle_MouseUpDown(object sender, MouseButtonEventArgs e)
        {
            var rect = sender as ActivityView;
            if (rect == null) throw new ArgumentException("sender must be an instance of ActivityView class");
            if (e.ButtonState == MouseButtonState.Pressed)
            {
                _mouseLeftPosition = e.GetPosition(listBox).X;
                _shift = 0;
                _currentActivityView = rect;

                
                if (e.GetPosition(rect).X >= rect.ActualWidth - 5)//TODO magic number
                {
                    _isResizeMode = true;
                }               
            }
            else if (e.ButtonState == MouseButtonState.Released)
            {                
                rect.RefreshLeft();
                rect.RefreshWidth();
                _currentActivityView = null;
                _currentActivityViewModel = null;
                _isResizeMode = false;                

            }
        }

        private void Rectangle_MouseMove(object sender, MouseEventArgs e)
        {            
            var rect = sender as ActivityView;
            if (rect == null) throw new ArgumentException("sender must be an instance of ActivityView class");            
            rect.Cursor = e.GetPosition(rect).X >= (rect.ActualWidth - 5) || _isResizeMode ? Cursors.SizeWE : Cursors.Arrow;            
        }

        private void listBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (_currentActivityView == null) return;
            
            if (e.LeftButton == MouseButtonState.Pressed)
            {
            	if (_currentActivityViewModel == null)
            	{
            		var item = listBox.SelectedItem;
            		var actvm = item as ActivityViewModel;
            		if (actvm == null) throw new Exception("WTF!?");
            		_currentActivityViewModel = actvm;
            	}
                if (_isResizeMode)
                {
                    var mouseLeftPosition = e.GetPosition(listBox).X;
                    var shift = mouseLeftPosition - _mouseLeftPosition;
                    var width = _currentActivityView.Width + shift;
                    _shift += shift;                    
                    if ((_currentActivityView.Width + _shift) >= 0)
                    {
                        var days = Math.Round(_shift/ViewConstants.PixelsPerDay);
                        if (days != 0)
                        {
                            var newPeriod = _currentActivityViewModel.Period.ResizeToLeft(days);
                            _commandsQueue.Do(new ChangeActivityCommand(_manager, _currentActivityViewModel, newPeriod));
                            _shift -= days * ViewConstants.PixelsPerDay;
                        }
                        _currentActivityView.Width = width;
                        
                    }
                    _mouseLeftPosition = mouseLeftPosition;
                }
                else
                {

                    var mouseLeftPosition = e.GetPosition(listBox).X;
                    var shift = mouseLeftPosition - _mouseLeftPosition;
                    _shift += shift;

                    var days = Math.Round(_shift/ViewConstants.PixelsPerDay);
                    if (days != 0)
                    {
                        var newPeriod = _currentActivityViewModel.Period.Shift(days);
                        _commandsQueue.Do(new ChangeActivityCommand(_manager, _currentActivityViewModel, newPeriod));
                        _shift -= days * ViewConstants.PixelsPerDay;
                    }
                    var left = Canvas.GetLeft(_currentActivityView) + shift;
                    Canvas.SetLeft(_currentActivityView, left);
                    _mouseLeftPosition = mouseLeftPosition;
                }
            }
        }

        private void listBox_MouseUp(object sender, MouseButtonEventArgs e)
        {            
            if (_currentActivityView != null)
            {
                _currentActivityView.Cursor = Cursors.Arrow;
                _currentActivityView.RefreshWidth();
                _currentActivityView.RefreshLeft();
            }
            _currentActivityView = null;
            _currentActivityViewModel = null;
            _isResizeMode = false;

        }

        private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var pos = e.GetPosition(listBox);
            Commands.CreateCommand.Execute(pos, this);
        }

        private void CommandBinding_CreateCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = _manager.Tasks.Count > 0 && _manager.Employees.Count > 0;
        }

        #endregion

        #endregion
      
        #region Fields

        private double _canvasHeight;

        private double _canvasWidth;

        private double _scrollWidth;

        private double _mouseLeftPosition;

        private double _shift;

        private ActivityView _currentActivityView;

        private ActivityViewModel _currentActivityViewModel;

        private bool _isResizeMode;

        private CommonManager _manager;

        private CommandsQueue _commandsQueue;

        private Modes _mode;

        private TaskViewModel _task;

        private EmployeeViewModel _employee;

        private bool _isDetailed;

        #endregion                

        public enum Modes
        {
            ByEmployees,
            ByTasks
        }

        
    }

    

    
    public class IndexToActivityTopConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var tn = (int)value;
            var margin = (int) (ViewConstants.PixelsPerLine - ViewConstants.ActivityHeight)/2;

            return tn * ViewConstants.PixelsPerLine + ViewConstants.TimeGridLabelPadding + margin;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }


    public class RoundDoubleConverter : IValueConverter
    {
        public static RoundDoubleConverter Instance { get; private set; }

        static RoundDoubleConverter()
        {
            Instance = new RoundDoubleConverter();
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var val = (double)value;

            if (parameter != null)
            {
                var param = int.Parse((string)parameter);
                var x = Math.Pow(10, param);
                return Math.Round(val*x)/x;
            }            
            return Math.Round(val);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }

    public class BoolToVisibilityConverter : IValueConverter
    {
        public static BoolToVisibilityConverter Instance { get; private set; }

        static BoolToVisibilityConverter()
        {
            Instance = new BoolToVisibilityConverter();
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var val = (bool)value;

            if (parameter != null)// && parameter is bool && (bool)parameter)
            {
                return !val ? Visibility.Visible : Visibility.Hidden;
            }
            return val ? Visibility.Visible : Visibility.Hidden;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }

    public class ActivityVisibilityMultiBindingConverter : IMultiValueConverter
    {
        public static ActivityVisibilityMultiBindingConverter Instance { get; private set; }

        static ActivityVisibilityMultiBindingConverter()
        {
            Instance = new ActivityVisibilityMultiBindingConverter();
        }      

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values == null || values.Length < 3) return null;
            var task = (TaskViewModel)values[0];
            var empl = (EmployeeViewModel)values[1];
            var act = (ActivityViewModel)values[2];
            if (act == null) return null;

            return act.Task == task || act.Employee == empl ? Visibility.Visible : Visibility.Hidden;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }



}
